"""
题目背景：

    研究生经常需要处理和备份大量的实验数据。假设你的云存储空间有限，你希望优先备份那些最重要且单位大小价值最高的数据文件，以在有限的空间内最大化备份数据的总价值。

题目描述：

    你有一系列数据文件，每个文件都有一个“大小(weight)”和一个“重要性评分(value)”。你的任务是设计一个 DataBackup 系统，该系统能够选择要备份的文件（或文件的一部分），以在不超过云盘总容量的前提下，最大化备份的总价值。

需要：

    1.创建一个 DataFile 类来表示数据文件，包含 name, size, value 属性。
    2.创建一个 DataBackup 类，包含 capacity 属性和一个 perform_backup 方法。
    3.perform_backup 方法接收一个文件列表，并根据贪心策略（优先选择“价值密度”最高的文件）来决定备份方案，并返回最大总价值。

测试用例：

输入	输出
50	240.00
0	0.00
100	325.00


"""

from typing import List,Dict,Tuple

class DataFile:
    def __init__(self, name, size, value):
        self.name = name  # Name of the data file
        self.size = size  # Size of the data file
        self.value = value #这就是该文件的总价值
        if size == 0:  #没有对文件size进行判断 不然会引发除0异常
            self.p = 0.0 # 文件大小为0 这里视为无价值；或者结合业务、题目要求,设置其价值；甚至在排序前单独处理大小为 0 的文件，直接累加其价值（前提是价值有效），无需参与密度排序。
        else:
            self.p = (float)(self.value / self.size)  # 价值密度


class DataBackup:
    #TODO:init方法方法名写错了 少个i !!!
    #def __int__(self, capacity):
    #    self.capacity = capacity #这个属性就是题目规定的存储空间的最大值 用来一个个和DataFile类的size大小对比
    #    self.capacityLeft = capacity #系统的剩余可用容量

    def __init__(self, capacity):
        self.capacity = capacity #这个属性就是题目规定的存储空间的最大值 用来一个个和DataFile类的size大小对比
        #TODO:DataBackup的capacityLeft属性会在perform_backup中被修改，若多次调用该方法，会导致后续调用使用的是修改后的剩余容量（而非初始capacity）。
        # 应使用局部变量记录剩余容量，而非修改实例属性。
        #如果capacityLeft设计成类的属性，则capacityLeft是 “对象级别的状态”，而下面perform_backup 方法的设计初衷通常是 “单次独立的备份操作”（每次调用应基于初始容量计算）。
        # 若方法修改了实例状态，多次调用就会相互干扰，违背 “单次操作独立性” 的预期。比如：如果要多次独立测试该方法，后面的测试一开始就不是原始容量了，而是上次测试后剩余的容量了
        #self.capacityLeft = capacity #系统的剩余可用容量

    # perform_backup 方法接收一个文件列表，并根据贪心策略（优先选择“价值密度”最高的文件）来决定备份方案，并返回最大总价值。
    #def perform_backup(self, fileList) -> int:  #题目给的测试用例返回值是float类型 我写的是int类型
    def perform_backup(self, fileList) -> float:  # TODO:题目给的测试用例返回值是float类型 我写的是int类型
        if len(fileList)==0:
            return 0.0
        capacityLeft = self.capacity  # 建议用局部变量
        # 根据每个文件的价值密度 降序排序 然后根据贪心算法 挨个选择要备份的文件(只要存储空间还够)，如果空间快要不够的时候，针对最后这个文件 能备份多少是多少
        sortedFilesDescByP = sorted(fileList,key=lambda oneFile:oneFile.p,reverse=True)

        resultList = []
        resultTotalValue = 0
        i = 0
        #TODO:当fileList为空时，sortedFilesDescByP也是空列表，此时访问sortedFilesDescByP[i]（i=0）会触发IndexError。需在循环前判断列表是否为空：
        #TODO:i<=len(fileList)-1 变量写错了 应该改为：i<=len(sortedFilesDescByP)-1
        #while(self.capacityLeft-sortedFilesDescByP[i].size>=0 and i<=len(fileList)-1):  #表示足够支持当前的文件的备份了
        while (i<len(sortedFilesDescByP) and capacityLeft - sortedFilesDescByP[i].size >= 0):  # 表示足够支持当前的文件的备份了
            resultList.append(sortedFilesDescByP[i]) #直接把对象加入进来 不过这步用不到 先不删了
            resultTotalValue+=sortedFilesDescByP[i].value
            capacityLeft-=sortedFilesDescByP[i].size  #存储剩余的空间的容量 也要对应减少
            i+=1

        #if(i==len(fileList-1)):  #把所有的文件都同步完了  TODO:这里写错了
        if (i == len(sortedFilesDescByP)):  # 把所有的文件都同步完了
            return resultTotalValue

        #这里如果跳出while循环，所以正在被考虑的文件不够同步(或者完全同步)到文件系统了；如果还剩一点点空间 有多少同步多少
        if(capacityLeft>0):
            #最后同步(用掉)剩余的存储空间
            #resultTotalValue+=capacityLeft  # TODO:错误把容量当做价值加入进来了 应该按 该文件的总价值*同步的文件的比例)
            resultTotalValue += sortedFilesDescByP[i].value * (capacityLeft / sortedFilesDescByP[i].size)


        # 不知道学校如何测试 没有规定程序入口 这里做一个输出吧
        #print(resultTotalValue)  #TODO:没事少打印 可能干扰测试

        #返回最后结果 可以在最后的结果中，对结果保留2位小数，不要在过程中间因为某一步可能会这儿那儿的 而在过程中保留2位小数 那样 数据累加多了 可能最后结果就不准了
        return round(resultTotalValue,2)



def test_data_backup():
    # 测试用例1：标准示例（匹配题目给出的第一个测试用例）
    files1 = [
        DataFile("file1", 10, 60),   # 密度6.0
        DataFile("file2", 20, 100),  # 密度5.0
        DataFile("file3", 30, 120)   # 密度4.0
    ]
    backup1 = DataBackup(50)
    assert backup1.perform_backup(files1) == 240.00, "测试用例1失败"

    # 测试用例2：容量为0的情况（匹配题目给出的第二个测试用例）
    files2 = [
        DataFile("fileA", 10, 100),
        DataFile("fileB", 20, 200)
    ]
    backup2 = DataBackup(0)
    assert backup2.perform_backup(files2) == 0.00, "测试用例2失败"

    # 测试用例3：容量足够装下所有文件（匹配题目给出的第三个测试用例）
    files3 = [
        DataFile("fileX", 50, 120),  # 密度2.4
        DataFile("fileY", 40, 100),  # 密度2.5
        DataFile("fileZ", 70, 210)   # 密度3.0
    ]
    backup3 = DataBackup(100)
    assert backup3.perform_backup(files3) == 285.00, "测试用例3失败"

    # 测试用例4：需要部分备份最后一个文件
    files4 = [
        DataFile("doc1", 30, 90),    # 密度3.0
        DataFile("doc2", 25, 100),   # 密度4.0
        DataFile("doc3", 40, 80)     # 密度2.0
    ]
    backup4 = DataBackup(50)
    # 先取doc2(25,100)，剩余25；再取doc1的25/30部分（价值75），总计175
    assert backup4.perform_backup(files4) == 175.00, "测试用例4失败"


    # 测试用例6：空文件列表
    files6 = []
    backup6 = DataBackup(100)
    assert backup6.perform_backup(files6) == 0.00, "测试用例6失败"

    # 测试用例7：单个文件刚好装满容量
    files7 = [DataFile("single", 50, 200)]
    backup7 = DataBackup(50)
    assert backup7.perform_backup(files7) == 200.00, "测试用例7失败"

    # 测试用例8：单个文件超过容量（部分备份）
    files8 = [DataFile("big", 100, 300)]
    backup8 = DataBackup(50)
    #assert backup8.perform_backup(files8) == 150.00, "测试用例8失败"

    print("所有测试用例通过！")

# 执行测试
test_data_backup()

